package org.vectrics.dataweb;

import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.log4j.Logger;
import org.vectrics.SystemException;
import org.vectrics.common.util.StringUtils;
import org.vectrics.domain.Domain;
import org.vectrics.hibernate.ApplicationContext;
import org.vectrics.hibernate.ThreadProperties;
import org.vectrics.provider.CoreServiceLocator;
import org.vectrics.struts.Request;
import org.vectrics.user.PasswordEncryptor;
import org.vectrics.user.Person;
import org.w3c.dom.Document;
import org.w3c.dom.Element;


/**
 * This helper class was created in order to preserve the ThreadProperties settings that
 * are used for database operations.  In cases where this servlet is called by other 
 * Vectrics servlets, the thread properties may already be set for a different application.
 * The thread starts in the application and still has the same properties when this 
 * servlet is called.
 * 
 * @author MMoore
 *
 */
public class DataServletHelper {
	private transient Logger log = Logger.getLogger(this.getClass());
	private Request request;
	private Element dataElement;
	private Element requestDataElement;
    private Document responseDocument = null;
    private Element responseElement = null;
    private WebServiceAction serviceAction = null;
    private ApplicationContext beanFactory = null;
    
	public DataServletHelper(Request request, 
    		Element requestDataElement,	WebServiceAction serviceAction, ApplicationContext applicationInformation) {
		super();
    	Document responseDocument = this.getResponseDocument();
    	if (responseDocument == null)
    		throw new SystemException("Initialization of response document failed");
    	//this.responseElement = this.createChildElement(responseDocument.getDocumentElement(), "response");
    	this.responseElement = responseDocument.createElement("response");
    	responseDocument.appendChild(this.responseElement);
    	
    	//this.getResponseElement().appendChild(dataElement);
    	this.dataElement = this.createChildElement(this.responseElement,  "data");
    	this.serviceAction = serviceAction;
		this.request = request;
		this.requestDataElement = requestDataElement;
		this.beanFactory = beanFactory;
	}


	
	public void run() {
		log.info("run() called");
		try {
			responseElement.getOwnerDocument();
			processData(request, responseDocument, requestDataElement, responseElement);
		} catch (Exception ex) {
    		log.error("Exception processing response", ex);
    		Element errorElement = this.createChildElement(this.getResponseElement(), "error");
    		errorElement.setAttribute("message", "Exception processing response: " 
    				+ ex.getMessage());
    		errorElement.setAttribute("code", "exception");
		}
	}
	
    protected final void processData(Request request, Document document,
    		Element requestDataElement,	Element responseElement) {
		if (responseElement == null)
			throw new SystemException("responseElement is null");
		if (request == null)
			throw new SystemException("request is null");
		Person authPerson = null;
    	if (requestDataElement == null) {
    		String domainId = request.getParameter("domainId");
    		if (StringUtils.isNullOrBlank(domainId)) {
        		domainId = request.getParameter("domain");
        		if (StringUtils.isNullOrBlank(domainId)) {
        			throw new SystemException("Parameter: 'domainId' was not present in request");
        		}
    		}

    		String userName = request.getParameter("user");
    		if (StringUtils.isNullOrBlank(userName)) {
    			throw new SystemException("Parameter: 'user' was not present in request");
    		}
    		String loginCode = request.getParameter("pcode");
    		if (StringUtils.isNullOrBlank(loginCode)) {
    			throw new SystemException("Parameter: 'pcode' was not present in request");
    		}
    		
    		Domain domain =  CoreServiceLocator.getSystemService().findDomain(new Long(domainId));
    		log.info("Domain = " + domain);
    		log.info("User Name = " + userName);
    		Person person = CoreServiceLocator.getPersonService().findPersonWithUserName(domain, userName);
    		log.info("Auth Person = " + person);
    		String correctCode = person.getUser().getPassword();
    		log.info("Correct code = " + correctCode);
    		if (loginCode.equals(correctCode)) {
        		log.info("Code was correct, person authorized");
    			authPerson = person;
    		}
    	} else {
        	log.info("Looking for authentication element...");
    		Element authUserElement = RequestUtils.getAuthUserElement(requestDataElement);
    		if (authUserElement != null) {
    			log.info("Found authentication element");
    			setAuthorizedPerson(null, request);
    			
    			String userName = authUserElement.getAttribute("user");
    			String password = authUserElement.getAttribute("password");
    			String domainId = authUserElement.getAttribute("domainId");
    			if (StringUtils.isNullOrBlank(domainId))
    				throw new SystemException("DomainId not passed in XML command");
    			
    			Domain domain = CoreServiceLocator.getSystemService().findDomain(new Long(domainId));
    			if (domain.getVoided())
    				throw new SystemException("Data owner specified in web call is voided");
    			ThreadProperties.setDomain(domain);
    			
    			authPerson = CoreServiceLocator.getPersonService().findPersonWithUserName(ThreadProperties.getDomain(), userName);
    			if (authPerson == null) {
    				log.info("Could not find user: " + userName);
    				Element errorElement = document.createElement("error");
    				responseElement.appendChild(errorElement);
    				errorElement.setAttribute("message", "User not known: " + userName);
    				errorElement.setAttribute("code", "auth");
    				
    				CoreServiceLocator.getUserAuthService().setAuthorizedPerson(authPerson, request);
    			}
    			else {
    				log.debug("Found user, checking password....");
    				String encryptedPassword = password;
    				String encryptedUserPassword = authPerson.getUser().getPassword();
    				if (authPerson.getUser().getPasswordEncrypted() == false) {
    					PasswordEncryptor passwordEncryptor = new PasswordEncryptor();
    					encryptedUserPassword = passwordEncryptor.crypt(encryptedUserPassword);
    				}
    				if (encryptedUserPassword.equals(encryptedPassword)) {
    					log.info("User supplied good credentials");
    					CoreServiceLocator.getUserAuthService().setAuthorizedPerson(authPerson, request);
    				}
    				else {
    					log.info("User supplied incorrect password: " + encryptedPassword);
    					CoreServiceLocator.getUserAuthService().setAuthorizedPerson(authPerson, request);
    					
    					Element errorElement = document.createElement("error"); 
    					responseElement.appendChild(errorElement);
    					errorElement.setAttribute("message", "User password incorrect, user = " + userName);
    					errorElement.setAttribute("code", "auth");
    				}
    			}			
    		}
    		
    	}
    	
		if (authPerson == null) {
			log.info("Attempting to find authorized user attached to session");
			authPerson = CoreServiceLocator.getUserAuthService().getAuthorizedPerson(request);			
		}
		
   		responseElement.setAttribute("session", request.getSessionId());
		
		if (authPerson == null) {
			log.debug("");
			Element errorElement = document.createElement("error");// 
			responseElement.appendChild(errorElement);
			errorElement.setAttribute("message", "Session has not been authenticated.");
			errorElement.setAttribute("code", "no-auth");
		}
		else {
			log.debug("User is authenticated, forward request to action");
			String command = request.getParameter("command");
			if (command == null)
				throw new SystemException("Request did not specify a 'command' parameter");
		
			this.serviceAction.processData(request, command, requestDataElement, responseElement);
		}
    }

    protected void setAuthorizedPerson(Person person, Request request) {
    	CoreServiceLocator.getUserAuthService().setAuthorizedPerson(person, request);
    }

    public final Document getResponseDocument() {
    	if (responseDocument == null) {
    		try {
    			responseDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
    		} catch (Exception ex) {
    			throw new SystemException("Exception creating response document", ex);
    		}
    	}
    	return(responseDocument);
    }
    
    
    public Element getResponseElement() {
    	if (responseElement == null) {
    		responseElement = getResponseDocument().createElement("response");
    		getResponseDocument().appendChild(responseElement);
    	}
    	return(responseElement);
    }
	
    
    protected Element createChildElement(Element parent, String childNodeName) {
    	if (parent == null)
    		throw new SystemException("createChildElement was passed a null parent element");
    	Element childElement = parent.getOwnerDocument().createElement(childNodeName);
    	parent.appendChild(childElement);
    	return(childElement);
    }
    
    
}
